import bpy
import os
from bpy.types import Operator, Panel
from bpy.props import StringProperty, CollectionProperty, EnumProperty, IntProperty, BoolProperty
from bpy_extras.io_utils import ImportHelper
import uuid

def create_material_with_image_sequence(material_name, image_paths, driver_object=None, driver_type='LOC_X', multiplier=1):
    """创建一个包含图像序列节点组的材质，并将其分配给选中的对象"""
    # 确保材质名称唯一
    original_name = material_name
    counter = 1
    while material_name in bpy.data.materials:
        material_name = f"{original_name}_{counter}"
        counter += 1
    
    # 创建新材质
    mat = bpy.data.materials.new(name=material_name)
    mat.use_nodes = True
    
    # 清理现有节点
    nodes = mat.node_tree.nodes
    nodes.clear()
    
    # 创建节点
    bsdf = nodes.new("ShaderNodeBsdfPrincipled")
    bsdf.name = "原理化 BSDF"
    bsdf.location = (-355, 132)
    
    output = nodes.new("ShaderNodeOutputMaterial")
    output.name = "材质输出"
    output.location = (35, 161)
    output.is_active_output = True
    
    image_texture = nodes.new("ShaderNodeTexImage")
    image_texture.name = "图像纹理"
    image_texture.location = (-893, 162)
    image_texture.extension = 'CLIP'  # 使用CLIP扩展模式避免边缘重复
    
    mapping = nodes.new("ShaderNodeMapping")
    mapping.name = "映射"
    mapping.location = (-1192, 137)
    
    tex_coord = nodes.new("ShaderNodeTexCoord")
    tex_coord.name = "纹理坐标"
    tex_coord.location = (-1443, 103)
    
    # 连接节点
    links = mat.node_tree.links
    links.new(bsdf.outputs[0], output.inputs[0])
    links.new(image_texture.outputs[0], bsdf.inputs[0])
    links.new(image_texture.outputs[1], bsdf.inputs[4])
    links.new(mapping.outputs[0], image_texture.inputs[0])
    links.new(tex_coord.outputs[2], mapping.inputs[0])
    
    # 设置图像序列
    if image_paths:
        # 加载第一张图像并设置为序列
        try:
            img = bpy.data.images.load(image_paths[0], check_existing=False)  # 不检查现有图像，确保创建新的
        except BaseException as e:
            return None
        
        img.source = 'SEQUENCE'
        
        # 设置图像序列属性
        image_texture.image = img
        image_texture.image_user.frame_duration = len(image_paths)
        image_texture.image_user.frame_offset =0
        image_texture.image_user.frame_start = 1
        image_texture.image_user.use_cyclic = True
        image_texture.image_user.use_auto_refresh = True

        # 如果指定了驱动对象，创建驱动
        if driver_object:
            driver = image_texture.image_user.driver_add('frame_offset')
            driver.driver.type = 'SCRIPTED'

            # 创建变量
            var = driver.driver.variables.new()
            var.name = "var"
            var.type = 'TRANSFORMS'
            var.targets[0].id = driver_object
            var.targets[0].transform_type = driver_type
            var.targets[0].transform_space = 'LOCAL_SPACE'
            
            # 在现有变量设置后添加时间帧变量
            time_var = driver.driver.variables.new()
            time_var.name = "frame"
            time_var.type = 'SINGLE_PROP'
            time_var.targets[0].id_type = 'SCENE'
            time_var.targets[0].id = bpy.context.scene
            time_var.targets[0].data_path = "frame_current"
            
            if multiplier==0:
                multiplier=1
            if multiplier > 0:
                expression = f"int(var) * {multiplier}%{len(image_paths)}-(frame-1)%{len(image_paths)}"
            else:
                expression = f"int(var) / {abs(multiplier)}%{len(image_paths)}-(frame-1)%{len(image_paths)}"
            driver.driver.expression = expression
    
    return mat

class MATERIAL_IMAGE_SEQUENCE_OT_select_images(Operator, ImportHelper):
    """选择图像序列文件"""
    bl_idname = "node.material_sequence_select_images"
    bl_label = "选择图像序列"
    bl_description = "选择用于图像序列的图像文件"
    
    # 过滤器：只显示图像文件
    filter_glob: StringProperty(
        default='*.png;*.jpg;*.jpeg;*.tiff;*.tif;*.bmp;*.exr',
        options={'HIDDEN'}
    )# type: ignore
    
    # 允许选择多个文件
    files: CollectionProperty(
        type=bpy.types.OperatorFileListElement,
        options={'HIDDEN', 'SKIP_SAVE'}
    )# type: ignore
    
    material_name: StringProperty(
        name="材质名称",
        description="创建的材质名称",
        default="M_Sequence",
    )# type: ignore
    
    driver_object_name: StringProperty(
        name="驱动对象名称",
        description="用于驱动图像序列偏移的对象名称",
        default="",
    )# type: ignore
    
    target_objects_names: StringProperty(
        name="目标对象名称列表",
        description="要添加材质的对象名称列表，用逗号分隔",
        default="",
    )# type: ignore
    
    driver_type: EnumProperty(
        name="驱动类型",
        description="选择用于驱动的变换类型",
        items=[
            ('LOC_X', "X位置", "使用X位置驱动"),
            ('LOC_Y', "Y位置", "使用Y位置驱动"),
            ('LOC_Z', "Z位置", "使用Z位置驱动"),
        ],
        default='LOC_Z',
    )# type: ignore
    

    
    multiplier: IntProperty(
        name="倍数",
        description="正数表示乘以，负数表示除以",
        default=1,
        min=-100,
        max=100,
    )# type: ignore
    
    single_mesh_object: BoolProperty(
        name="单个网格物体",
        description="标识是否为单个网格物体的情况",
        default=False,
    )# type: ignore

    def execute(self, context):
        # 获取选中的文件
        directory = os.path.dirname(self.filepath)
        image_paths = []
        
        # 收集所有选中的图像文件路径
        for file_elem in self.files:
            filepath = os.path.join(directory, file_elem.name)
            image_paths.append(filepath)
        
        # 对图像路径进行排序，确保序列正确
        image_paths.sort()
        
        # 获取驱动对象（仅在非单个网格物体情况下）
        driver_object = None
        if not self.single_mesh_object and self.driver_object_name:
            driver_object = bpy.data.objects.get(self.driver_object_name)
        
        # 创建材质
        mat = create_material_with_image_sequence(
            self.material_name, 
            image_paths, 
            driver_object,
            self.driver_type,
            self.multiplier
        )
        
        if mat==None:
            self.report({'ERROR'}, "创建材质失败")
            return {'CANCELLED'}
        
        # 将材质分配给目标对象（仅在非单个网格物体情况下）
        if not self.single_mesh_object and self.target_objects_names:
            target_names = self.target_objects_names.split(',')
            for obj_name in target_names:
                obj = bpy.data.objects.get(obj_name.strip())
                if obj and obj.type == 'MESH':
                    if obj.data.materials:
                        obj.data.materials[0] = mat
                    else:
                        obj.data.materials.append(mat)
        # 如果是单个网格物体，则为该物体添加材质
        elif self.single_mesh_object and self.driver_object_name:
            obj = bpy.data.objects.get(self.driver_object_name)
            if obj and obj.type == 'MESH':
                if obj.data.materials:
                    obj.data.materials[0] = mat
                else:
                    obj.data.materials.append(mat)
        
        if self.single_mesh_object:
            self.report({'INFO'}, f"已创建材质 {mat.name} 并添加到对象 {self.driver_object_name}")
        else:
            self.report({'INFO'}, f"已创建材质 {mat.name}，驱动对象: {self.driver_object_name}，目标对象数: {len(self.target_objects_names.split(',')) if self.target_objects_names else 0}")
        return {'FINISHED'}
    
    def invoke(self, context, event):
        # 打开文件浏览器
        self.filepath=""
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}
    
    def draw(self, context):
        layout = self.layout
        box = layout.box()
        box.label(text=f"材质名称:{self.material_name}")
        box.label(text="驱动设置")
        if self.single_mesh_object:
            box.label(text="模式: 单个网格物体（无驱动）")
            return
        if not self.target_objects_names:
            box.label(text="模式: 单个非网格物体（有驱动）")
        else:
            box.label(text="模式: 多个网格物体（有驱动）")
        box.label(text=f"倍数: {self.multiplier}")
        box.label(text=f"驱动类型: {self.driver_type.replace('LOC_','')}位置")




class MATERIAL_IMAGE_SEQUENCE_OT_main(Operator):
    """主操作符，显示材质名称和驱动设置对话框"""
    bl_idname = "node.material_sequence"
    bl_label = "创建图像序列"
    bl_description = "创建一个用于图像序列的节点组与控制对象"
    bl_options = {'REGISTER', 'UNDO'}
    
    material_name: StringProperty(
        name="材质名称",
        description="创建的材质名称",
        default="M_Sequence",
    )# type: ignore
    
    driver_object_name: StringProperty(
        name="驱动对象名称",
        description="用于驱动图像序列偏移的对象名称",
        default="",
    )# type: ignore
    
    target_objects_names: StringProperty(
        name="目标对象名称列表",
        description="要添加材质的对象名称列表，用逗号分隔",
        default="",
    )# type: ignore
    
    driver_type: EnumProperty(
        name="驱动类型",
        description="选择用于驱动的变换类型",
        items=[
            ('LOC_X', "X位置", "使用X位置驱动"),
            ('LOC_Y', "Y位置", "使用Y位置驱动"),
            ('LOC_Z', "Z位置", "使用Z位置驱动"),
        ],
        default='LOC_Z',
    )# type: ignore
    
    multiplier: IntProperty(
        name="倍数",
        description="正数表示乘以，负数表示除以",
        default=1,
        step=10,
        min=-100,
        max=100,
    )# type: ignore
    
    single_mesh_object: BoolProperty(
        name="单个网格物体",
        description="标识是否为单个网格物体的情况",
        default=False,
    )# type: ignore
    
    @classmethod
    def poll(cls, context):
        # 至少需要一个选中的物体
        return len(context.selected_objects) >= 1
    
    
    def execute(self, context):
        # 根据预处理的结果调用相应的操作
        if self.single_mesh_object:
            # 单个网格物体：不创建驱动，直接添加材质
            bpy.ops.node.material_sequence_select_images(
                'INVOKE_DEFAULT',
                material_name=self.material_name,
                driver_object_name=self.driver_object_name,
                single_mesh_object=True,
                driver_type=self.driver_type,
                multiplier=self.multiplier
            )
        elif len(context.selected_objects) == 1:
            # 单个非网格物体：创建驱动但不添加材质到任何物体
            bpy.ops.node.material_sequence_select_images(
                'INVOKE_DEFAULT',
                material_name=self.material_name,
                driver_object_name=self.driver_object_name,
                single_mesh_object=False,
                driver_type=self.driver_type,
                multiplier=self.multiplier
            )
        else:
            # 多个物体：使用原来的逻辑
            bpy.ops.node.material_sequence_select_images(
                'INVOKE_DEFAULT',
                material_name=self.material_name,
                driver_object_name=self.driver_object_name,
                target_objects_names=self.target_objects_names,
                single_mesh_object=False,
                driver_type=self.driver_type,
                multiplier=self.multiplier
            )
        return {'FINISHED'}
    
    def invoke(self, context, event):
        """准备物体数据，返回驱动对象和目标对象列表"""
        
        active_object = context.active_object
        if not active_object:
            self.report({'ERROR'}, "没有活动物体！")
            return {'CANCELLED'}
        
        target_objects = context.selected_objects
        target_objects.remove(active_object)
        target_objects = [obj for obj in target_objects if obj.type == 'MESH']
        self.driver_object_name = active_object.name
        
        # 只有一个选中
        if len(target_objects) ==0:
            self.single_mesh_object = (active_object.type == 'MESH')
        else:
            self.single_mesh_object = False
            self.target_objects_names = ','.join([obj.name for obj in target_objects])

        self.index = 0
        while self.material_name in bpy.data.materials:
            self.material_name= f"M_Sequence_{self.index}"
            self.index += 1
        
        # 初始调用，显示设置对话框
        return context.window_manager.invoke_props_dialog(self)
    
    def draw(self, context):
        layout = self.layout
        layout.prop(self, "material_name")
        
        # 显示驱动对象
        box = layout.box()
        box.label(text="驱动设置")
        
        # 单个网格物体情况的特殊显示
        if self.single_mesh_object:
            box.label(text="模式: 单个网格物体（无驱动）")
            box = layout.box()
            box.label(text="目标对象")
            box.label(text=f"目标对象: {self.driver_object_name}")
        else:
            box.label(text=f"驱动对象: {self.driver_object_name}")
            # 显示目标对象
            box = layout.box()
            box.label(text="目标对象")
            if self.target_objects_names:
                target_names = self.target_objects_names.split(',')
                for name in target_names:
                    box.label(text=f"- {name.strip()}")
                box.label(text=f"共 {len(target_names)} 个对象")
            else:
                box.label(text="无目标对象（仅驱动）")
            
            # 驱动设置
            box = layout.box()
            box.prop(self, "driver_type")
            row = box.row()
            row.prop(self, "multiplier")




# 面板类，用于在UI中显示操作按钮
class NODE_PT_material_sequence(Panel):
    bl_label = "图像序列材质"
    bl_space_type = 'NODE_EDITOR'
    bl_region_type = 'UI'
    bl_category = "工具"
    
    def draw(self, context):
        layout = self.layout
        layout.operator("node.material_sequence")
        

sequence_classes = [
    MATERIAL_IMAGE_SEQUENCE_OT_select_images,
    MATERIAL_IMAGE_SEQUENCE_OT_main,
    NODE_PT_material_sequence,
]

def sequence_register():
    for cls in sequence_classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.material_sequence_name = StringProperty(
        name="材质名称",
        description="创建的材质名称",
        default="M_Sequence",
    )
    
def sequence_unregister():
    for cls in reversed(sequence_classes):
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.material_sequence_name